#include <jni.h>

#include <stdio.h>
#include <dlfcn.h>

#include <android/log.h>
#define  LOG_TAG "voicecodec.cpp"
#define  D(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

static int index_adjust[8] = 
{
	-1, -1, -1, -1, 2, 4, 6, 8
};

static int step_table[89] =
{
	    7,     8,     9,    10,    11,    12,    13,    14,    16,    17, 
	   19,    21,	   23,    25,    28,    31,    34,    37,    41,    45,
	   50,    55,    60,    66,	   73,    80,    88,    97,   107,   118, 
	  130,   143,   157,   173,   190,	 209,   230,   253,   279,   307,
	  337,   371,   408,   449,   494,	 544,   598,   658,   724,   796,
	  876,   963,  1060,  1166,  1282,  1411,  1552,  1707,  1878,  2066,
	 2272,  2499,  2749,  3024,  3327,  3660,  4026,  4428,  4871,  5358, 
	 5894,  6484,  7132,	7845,  8630,  9493, 10442, 11487, 12635, 13899,
	15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767,
};

static int encode_index = 0;
static int decode_index = 0;
static int prev_sample = 0;
static int cur_sample = 0;

void adpcm_reset()
{
	encode_index = 0;
	decode_index = 0;
	prev_sample = 0;
	cur_sample = 0;
}

char adpcm_encode_16bit(short input)
{
	int sb, code;
	int delta;
	int output = 0;
	
	delta = input - prev_sample;
	if( delta < 0 ){
		delta = -delta;
		sb = 8;
	}
	else
		sb = 0;
	code = 4 * delta / step_table[encode_index];
	if( code > 7 )
		code = 7;
	
	encode_index += index_adjust[code];
	if( encode_index < 0 ){
		encode_index = 0;
	}
	else if( encode_index > 88 ){
		encode_index = 88;
	}
	prev_sample = input;
	
	return (sb|code);
}

short adpcm_decode_16bit(int input)
{
  int sb;
  int delta;
  int output;
  
  sb = (input >> 3) & 1;
	input &= 7;
  delta = (step_table[decode_index]*input)/4+step_table[decode_index]/8;
  if( sb == 1){
  	delta = -delta;
  }
  cur_sample += delta;
  if( cur_sample > 32767){
  	output = 32767;
  }
  else if( cur_sample < -32768 ){
  	output = -32768;
  }
  else{
  	output = cur_sample;
  }
  cur_sample = output;
  
  decode_index += index_adjust[input];
  if( decode_index < 0){
  	decode_index = 0;
  }
  if( decode_index > 88){
  	decode_index = 88;
  }
  
	return output;
}

extern "C" {

JNIEXPORT jint JNICALL Java_faircat_android_exphone_VoiceCodec_OutputEncode(JNIEnv *env, jobject obj, jbyteArray input, jint len, jint rate, jbyteArray output, jint len_out)
{
	typedef short in_type;
	in_type *in_buf;
	typedef char out_type;
	out_type *out_buf;
	int sz;
	int out_sz;
	
	in_buf = (in_type *)env->GetByteArrayElements(input, NULL);	
	sz = len / sizeof(in_type);
	out_sz = sz / 2;
	
	out_buf = (out_type *)env->GetByteArrayElements(output, NULL);
	
	out_type *out_buf1 = out_buf;
	in_type *in_buf1 = in_buf;
	for( int i = 0; i < out_sz; ++i ){	  
		*out_buf1++ = (adpcm_encode_16bit(*in_buf1) << 4) | adpcm_encode_16bit(*(in_buf1+1));
		in_buf1 += 2;
		//*out_buf1++ = (*in_buf1++)>>8;
	}
	
	env->ReleaseByteArrayElements(input, (jbyte *)in_buf, 0);
	env->ReleaseByteArrayElements(output,(jbyte *)out_buf, 0);
	
	return out_sz * sizeof(out_type);
}

JNIEXPORT jint JNICALL Java_faircat_android_exphone_VoiceCodec_InputDecode(JNIEnv *env, jobject obj, jbyteArray input, jint len, jint rate, jbyteArray output, jint len_out)
{
	typedef char in_type;
	in_type *in_buf;
	typedef short playback_type;
	playback_type *out_buf;
	int sz;
	int out_sz;
	
	in_buf = (in_type *)env->GetByteArrayElements(input, NULL);	
	sz = len / sizeof(in_type);
	out_sz = sz * 2;
	out_buf = (playback_type *)env->GetByteArrayElements(output, NULL);
	
	playback_type *out_buf1 = out_buf;
	in_type *in_buf1 = in_buf;
		
	for( int i = 0; i < sz; ++i ){
		*out_buf1++ = adpcm_decode_16bit((*in_buf1) >> 4);
		*out_buf1++ = adpcm_decode_16bit((*in_buf1) & 0x0F);
		in_buf1 ++;		
		//*out_buf1++ = (*in_buf1++)<<8;
	}
	
	env->ReleaseByteArrayElements(input, (jbyte *)in_buf, 0);
	env->ReleaseByteArrayElements(output, (jbyte *)out_buf, 0);	
	return out_sz * sizeof(playback_type);
}

}
